查看原文
其他

技术分析|在 Nginx 服务器启用 GZip 压缩的实践

姚同学 明源云天际PaaS平台 2022-09-10


源宝导读:所有现代浏览器都支持 gzip 压缩编码,并会为所有 HTTP 请求自动协商此类压缩编码的资源。启用 gzip 压缩可大幅缩减所传输的资源文件响应的大小(最多可缩减 90%),从而显著缩短下载相应资源所需的时间、减少客户端的流量消耗并加快网页的首次呈现。本文就介绍一下 DevOps 平台如何在 nginx 服务器上启用 gzip 压缩,优化前端性能。


一、背景

Nginx 是一款性能强大的服务器,因为 nginx 服务器提供了很多对优化 Web 性能有帮助的功能,对静态资源提供 gzip 压缩就是其中之一。启用 gzip 压缩后,可以显著的加快网页的首次呈现速度。对于天际-DevOps平台(简称 RDC)这种单网页应用(SPA)来说,加快网页资源速度的意义是不言而喻的。而 DevOps 平台的服务端正是使用的 nginx 服务器,所以为了充分地优化前端性能,DevOps 平台在 nginx 服务器上启用了 gzip 压缩。

(一)启用 GZip 压缩的适用场景和注意事项

GZip 是一种可以作用于任何字节流的通用压缩程序。它会在后台记忆一些之前看到的内容,并尝试以高效方式查找并替换重复的数据片段,从而达到较好的压效果。但使用 gzip 压缩的最大错误之一就是用 gzip 压缩页面中的每个资源文件。

(二)适合 GZip 压缩的资源类型

实际上,gzip 主要用于对文本类型的资源进行压缩,例如常用见的文本资源:

  • HTML 文件:text/html(nginx 服务器默认就会压缩)、application/xhtml+xml
  • CSS 文件:text/css
  • JS 文件:application/x-javascript、application/javascript、text/javascript
  • JSON 文件(或者API请求结果):application/json、application/geo+json、application/ld+json application/manifest+json、application/x-web-app-manifest+json
  • XML 文件:application/xml、application/atom+xml、application/rdf+xml、application/rss+xml
  • SVG 文件:image/svg+xml;

除了常用的文本文件,gzip 也支持压缩以下 MIME 类型的文件:

  • application/vnd.ms-fontobject
  • application/wasm
  • font/eot
  • font/otf
  • font/ttf
  • image/bmp
  • text/cache-manifest
  • text/calendar
  • text/markdown
  • text/plain
  • text/vcard
  • text/vnd.rim.location.xloc
  • text/vtt
  • text/x-component
  • text/x-cross-domain-policy

GZip 对基于文本的内容的资源压缩效果最好,在压缩较大文件时往往可实现高达 70-90% 的压缩率,而如果对已经通过替代算法压缩过的资源(例如,大多数图片格式)运行 gzip,则效果甚微,甚至毫无效果。

(三启用合适的压缩比

Nginx 服务器中 gzip 压缩比的配置项是:gzip_comp_level,可选的值为 1-9,数值越高压缩比也就越高。但实际的应用实践中需要按照自己的实际情况来配置压缩比。因为启用 gzip 压缩,主要消耗的是服务器的 CPU 资源。压缩比越高,服务器对 CPU 资源的消耗也就越高。如果服务器的 CUP 配置不高,采用过高的压缩比反而不好,可能会导致服务器 CPU 的占用率太高,从而导致 NGINX 变慢或者卡顿。

如果没有特别高的要求,建议的 gziip 压缩比值为 5,即可保证压缩的效果(对大多 ASCII 编码的文件可以达到75%的压缩比),同时对 CPU 资源的损耗也比较低。并且通过在 DevOps 平台的实践中实测的结果,压缩比设置为 6-9,压缩的效果与 5 相比效果并不明显,只会增加对 CPU 资源的消耗。所以 DevOps 平台 nginx 服务器 gzip 配置的压缩等级也为 5。

最后,nginx 服务器对 gzip 的配置项中,有一项是:gzip_min_length 用来配置启用 gzip 压缩文件的最小体积。因为 gzip 压缩对原本体积就很小的资源文件压缩的效果也并不好,甚至可能出现使用 gzip 压缩体积很小的文件,压缩后的体积反而比压缩之前更大。所以通常会设置 gzip_min_length 1k,只有文件的体积超过了 1k,服务器才启用 gzip 压缩此文件。

(四浏览器对 GZip 压缩编码的支持情况

如图所示,目前所有主流的现代浏览器都支持 gzip 压缩。并且浏览器在发送请求时会自动请求该压缩编码的格式,如下图的请求头信息所示:

浏览器会自动在请求头(Reqeust Headers)信息中,添加 Accept-Encoding: gzip, deflate, br,向服务器请求压缩编码格式(gzip 压缩编码格式就在其中)的文件。

(五)确定 Nginx 服务器是否支持 GZip

如果你是使用 CentOS (本文以 CentOS 为例)或者使用其它 linux 服务器,通过包管理工具(yum、apt-get、dnf等)安装的 nginx,默认就开启了对 gzip 模块的支持。nginx 中的 gzip 处理模块是:ngx_http_gzip_module。可以使用:nginx -V 命令,查看 nginx 服务器是否开启了对 gzip 的支持模块:

如果显示如上图所示,显示了 –with-http_gzip_static_module,就说明使用的 nginx 服务器已经支持 gzip 了,可以开始配置 gzip 压缩了。


二、源码编译安装 Nginx

如果使用的 nginx 没有开启 ngx_http_gzip_module 模块,那么就需要使用源码方式重新编译安装 nginx,并配置开启 ngx_http_gzip_module 模块。

第1步:安装编译 nginx 的依赖包

源码编译安装 nginx 需要先安装按章编译相关的依赖包,命令如下:

sudo yum install gcc libpcre3 libpcre3-dev openssl libssl-dev libssl0.9.8 perl libperl-dev

第2步:下载 nginx

根据你的需要下载相应的 nginx 版本,命令如下:

wget http://nginx.org/download/nginx-1.11.2.tar.gz

第3步:进行编译安装前的配置

解压下载的文件,根据自己的情况解压到指定目录:

# 解压要 /usr/src,根据实际情况调整路径 tar -xzvf nginx-1.11.2.tar.gz -C /usr/src

然后跳转到解压后的目录

cd /usr/src/nginx-1.11.2

接着配置生成 makefile,如果需要添加第三方模块,使用 –add-module=/path/to/module 的方法编译,安装地址是 /etc/nginx,这个要根据你的实际情况配置:

# 根据实际情况配置,本示例只写出了启用 gzip 的配置./configure --prefix=/etc/nginx --with-http_gzip_static_module

最后就是是编译安装了:

# make 是生成在objs目录中,make install 则安装到 prefix 所示的目录中 make && make install

没有错误出现的话,就可以进入指定的存放 nginx 配置文件的目录(/etc/nginx)进行配置了。当然,手动源码编译安装完成后,还是可以使用 nginx -V 命令,确定是否如上图所示那样,已经正确开启 nginx 服务器的 gzip 压缩支持模块,在输入的配置信息中显示了 –with-http_gzip_static_module


三、配置 GZip 压缩

在确定支持 gzip 后,就可以进行 gzip 压缩相关的配置了。使用 vim 编辑器打开 nginx.conf 配置文件:

sudo vim /etc/nginx/nginx.conf

打开文件后,按 i 键进入编辑模式,在 http 块中输入以下配置信息:

# gzip 可以在 http, server, location 中和配置,这里配置到 http 下是全局配置,# 只要是使用当前 nginx 服务器的站点都会开启 gziphttp { gzip on; gzip_comp_level 5; gzip_min_length 1k; gzip_buffers 4 16k; gzip_proxied any; gzip_vary on; gzip_types application/javascript application/x-javascript text/javascript text/css text/xml application/xhtml+xml application/xml application/atom+xml application/rdf+xml application/rss+xml application/geo+json application/json application/ld+json application/manifest+json application/x-web-app-manifest+json image/svg+xml text/x-cross-domain-policy; gzip_static on; gzip_disable "MSIE [1-6]\."; }

每个配置项的说明如下:

  • gzip on;:开启 gzip,Default: off
  • gzip_comp_level 5;:压缩级别:1-9。5 是推荐的压缩级别,Default: 1
  • gzip_min_length 1k;:gzip 压缩文件体积的最小值。如果文件已经足够小了,就不需要压缩了,因为即便压缩了,效果也不明显,而且会占用 CPU 资源。Default: 20
  • gzip_buffers 4 16k;:设置用于压缩响应的 number 和 size 的缓冲区。默认情况下,缓冲区大小等于一个内存页。根据平台的不同,它也可以是4K或8K。
  • gzip_proxied any;:是否开启对代理资源的压缩。很多时候,nginx 会作为反向代理服务器,实际的静态资源在上有服务器上,只有开启了 gzip_proxied 才会对代理的资源进行压缩。Default: off
  • gzip_vary on;:每当客户端的 Accept-Encoding-capabilities 头发生变化时,告诉代理缓存 gzip 和常规版本的资源。避免了不支持 gzip 的客户端(这在今天极为罕见)在代理给它们 gzip 版本时显示乱码的问题。如果指令gzip, gzip_static 或 gunzip 处于活动状态, 则启用或禁用插入“ Vary:Accept-Encoding”响应标头字段。Default: off
  • gzip_types:压缩文件的 MIME 类型。`text/html` 默认就会开启 gzip 压缩,所以不用特别显示配置 `text/html` 的 MIME 类型。Default: text/html
  • gzip_static on;:服务器开启对静态文件( CSS, JS, HTML, SVG, ICS, and JSON)的压缩。但是,要使此部分与之相关,需要在 gzip_types 设置 MIME 类型,仅仅设置 gzip_static 为 on 是不会自动压缩静态文件的。
  • gzip_disable “MSIE [1-6]\.”;:IE6 以下的浏览器禁用 gzip 压缩。

更加推荐的一种做法是将 gzip 的配置保存到独立的配置文件中,例如:gzip.conf。然后在需要启用 gzip 压缩的地方,例如上面的代码在 nginx.conf 文件中引用 gzip.conf 文件:

http { # 引用 gzip 配置,本例是在 http 模块中调用,表示所有通过此 nginx 服务器配置的站点 # 将全部都开启 gzip 压缩 include path/to/gzip.conf;}

这种处理方式的好处是以后只用在 gzip.conf 文件中调整配置就可以了,并且调整配置后,所有调用 gzip.conf 配置的地方就一次性全部都调整了。

在完成 gzip 的配置以后,按冒号“:”键,输入 wq,保存配置并退出 vim 编辑器。然后输入重启 nginx 命令:

# 或者 sudo service nginx restartsudo service nginx reload

当然,如果你不放心 gzip 相关的配置是否编写正确,也可以使用 nginx -t 检测 nginx.conf 配置是否正确。


四、验证 GZip 压缩配置是否生效

服务端在接受到来自客户端的请求申请头部信息:Accept-Encoding: gzip, deflate, br 后,会对请求的资源响应内容的实体进行相应的编码处理,并且会在服务端的响应头部信息返回相应响应头部信息:

  • content-type: application/javascript; charset=utf-8:表示返回的数据的 MIME 类型是 application/javascript(js文件);
  • content-encoding: gzip:表示该文件采用了 gzip 压缩编码,这表明对 JS 文件的 gzip 压缩配置已经生效;
  • vary: Accept-Encoding:(配置说明中提到的)当客户端的 Accept-Encoding-capabilities 头发生变化时,告诉代理缓存 gzip 和常规版本的资源。

而浏览器在接收到服务器返回的 content-encoding: gzip 响应头后,会对采用 gzip 编码的实体内容进行解码。

看到 vary: Accept-Encoding 的相应头信息,表明在之前 gzip.conf 文件配置的 gzip_proxied any; 和 gzip_vary on; 也起作用了。DevOpes 平台的站点是通过 nginx 的反向代理配置访问上游服务器集群的:

# 站点的配置location / { # 代理到名为 host 的负载均衡集群 proxy_pass http://host; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}

因为在配置了 gzip_vary on  gzip_proxied any。在指令 gzip 处于活动状态, 则会在服务端返回的响应头信息中插入“ Vary:Accept-Encoding”响应标头字段,表明服务器开启了对代理资源的 gzip 压缩。


五、启用 GZip 压缩的效果

以下是经过 gzip 压缩后,DevOps 平台前端使用的关键 .js 脚本文件的压缩比统计表:

内容库
大小
压缩后大小
压缩比率
app.8a868929.js
1.2M
283 KB
76.9%
chunk-elementUI.4f7e0b4e.js
387 KB
104 KB
73.1%
chunk-libs.2df74eb7.js
2M
742 KB
70%
runtime-e9da8ceb.js
22.8 KB
6.3 KB
72.3%

根据统计的结果,在 nginx 服务器启动压缩后的压缩比都在 70% 以上,压缩效果还是很明显的。

再结合之前使用 preload / prefetch 预加载了这些关键资源和路由懒加载等一系列的优化方案,目前 DevOps 平台 通过天眼系统统计到的 P90 网页加载速度都在 1.7s 左右。启用 gzip 压缩,除了优化了页面的加载速度,提升了前端性能。对于公司来讲,启用 gzip 压缩后,DevOps 平台对于网络带宽的需求也降低了,或者说是在现有的带宽情况下,能够更加充分的利用带宽资源,从长期效益来看,也可以间接的降低公司在带宽上的运行成本。


六、后续的思考

除了 gzip 压缩外,还有另外一个压缩方式,而且压缩效果比 gzip 更好,而它就是 google 开发的 brotli 。目前大多数的主流浏览器(IE11 除外)也都支持 brotli 压缩格式的编码。后续可以考虑在 nginx 上同时开启 brotli 和 gzip 压缩。理论上同时开启两种压缩,浏览器是可以兼容处理的,在支持 brotli 的浏览器上,就是有限使用 br 编码,如果不支持就是使用 gzip 编码。

当然,在服务器端启用压缩,是会消耗服务器的 CPU 资源的。同时启用两种压缩,对 CPU 资源的消耗也就更大,我们还需要进一步验证。


------ END ------
作者简介
姚同学: 研发工程师,目前负责DevOps平台相关工作。

也许您还想看:
技术分享|NodeJS分布式链路追踪实现
技术分享 | 浏览器预加载资源技术的应用实践

更多明源云·天际开放平台场景案例与开发小知识,可以关注明源云天际开发者社区公众号:
【建模】用户自定义搜索条件,检索效率快人一步
【建模】列表编辑模式,你知道多少?
【Dveops】开发者如何申请开发狗?

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存